package cuppics;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.junit.Test;

public class Prj35 {

	/**
	 * The number, 197, is called a circular prime because all rotations of the
	 * digits: 197, 971, and 719, are themselves prime.
	 * 
	 * There are thirteen such primes below 100: 2, 3, 5, 7, 11, 13, 17, 31, 37,
	 * 71, 73, 79, and 97.
	 * 
	 * How many circular primes are there below one million?
	 */
	@Test
	public void test() {
		System.out.println(Calculator.calculateList(1000000));
		// System.out.println(Calculator.calculate(1000000));
	}

	public static class Calculator {

		public static int calculateList(int upLimit) {
			List<Integer> list = new ArrayList<Integer>();

			for (int i = 2; i <= upLimit; i++) {
				if (checkIsPrime(i)) {
					list.add(i);
				}
			}

			Set<Integer> primeSet = filterPrimes(list);

			List<Integer> ret = new ArrayList<Integer>();

			Iterator<Integer> iterator = primeSet.iterator();
			while (iterator.hasNext()) {
				int val = iterator.next();
				if (val < 10) {
					ret.add(val);
					continue;
				}

				int bit = 1;
				int tmpVal = val;
				while (tmpVal >= 10) {
					bit++;
					tmpVal /= 10;
				}

				boolean filter = false;
				tmpVal = val;
				for (int i = 0; i < bit; i++) {
					tmpVal = (int) (tmpVal % Math.pow(10, bit - 1) * 10 + tmpVal
							/ Math.pow(10, bit - 1));
					if (!primeSet.contains(tmpVal)) {
						filter = true;
						break;
					}

				}

				if (!filter) {
					ret.add(val);
				}

			}

			System.out.println(ret);
			return ret.size();
		}

		private static Set<Integer> filterPrimes(List<Integer> primeList) {
			Set<Integer> set = new HashSet<Integer>();

			for (Integer val : primeList) {

				if (val < 10) {
					set.add(val);
					continue;
				}

				int tmpVal = val;
				boolean hasZeroTwoFive = false;
				while (tmpVal >= 10) {
					tmpVal /= 10;
					// 103 031 310
					if (tmpVal % 10 == 0 || tmpVal % 10 == 2
							|| tmpVal % 10 == 4 || tmpVal % 10 == 6
							|| tmpVal % 10 == 8 || tmpVal % 10 == 5) {
						hasZeroTwoFive = true;
						break;
					}
				}

				if (!hasZeroTwoFive) {

					set.add(val);
					continue;
				}
			}

			return set;
		}

		private static boolean checkIsPrime(int val) {

			int num = Math.abs(val);

			if (num >= 10) {

				for (int i = 2; i <= Math.sqrt(num); i++) {
					if (num % i == 0) {
						return false;
					}
				}
				return true;

			} else {
				if (num == 2 || num == 3 || num == 5 || num == 7) {
					return true;
				}

				return false;
			}

		}

		
		public static int calculate(int upLimit) {

			BitSet bitMap = new BitSet(upLimit + 1);
			bitMap.set(0);
			bitMap.set(1);

			int startId = 0;
			while (true) {

				startId = getNext(startId + 1, upLimit, bitMap);

				System.out.println(startId);
				if (startId == -1) {
					break;
				}

				for (int i = startId + 1; i < upLimit; i++) {
					if (i % startId == 0) {
						setBit(i, true, bitMap);
					}
				}

			}

			checkCircular(bitMap, upLimit);

			int count = 0;
			int fromId = 0;
			while (-1 != (fromId = bitMap.nextClearBit(fromId))) {
				count++;
			}

			return count;
		}
		
		private static void checkCircular(BitSet bitMap, int upLimit) {

			int startId = 0;
			for (;;) {

				startId = bitMap.nextClearBit(startId);

				if (startId < 10) {
					continue;
				} else {

					int val = startId;
					int bit = 1;
					int tmpVal = val;
					boolean hasZeroTwoFive = false;
					while (tmpVal >= 10) {
						bit++;
						tmpVal /= 10;
						// 103 031 310
						if (tmpVal % 10 == 2 || tmpVal % 10 == 0
								|| tmpVal % 10 == 5) {
							hasZeroTwoFive = true;
							break;
						}
					}

					if (hasZeroTwoFive) {

						setBit(startId, true, bitMap);
						continue;
					}

					boolean isCircular = true;
					for (int i = 0; i < bit; i++) {
						val = (int) (val % Math.pow(10, bit - 1) * 10 + val
								/ Math.pow(10, bit - 1));

						if (bitMap.get(val)) {
							isCircular = false;
							break;
						}
					}

					if (!isCircular) {
						val = startId;
						for (int i = 0; i < bit; i++) {
							val = (int) (val % Math.pow(10, bit - 1) * 10 + val
									/ Math.pow(10, bit - 1));
							if (val < upLimit) {

								bitMap.set(val);
							}
						}
						continue;
					}

				}

			}

		}

		public static void setBit(int id, boolean isPrime, BitSet bitMap) {
			bitMap.set(id, isPrime);
		}

		public static int getNext(int startId, int upLimit, BitSet bitMap) {

			return bitMap.nextClearBit(startId);
		}

	}
}
